<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="ozZWt" id="ozZWt"><span data-lake-id="u8c896ff4" id="u8c896ff4">典型回答</span></h1>
  <p data-lake-id="u9ffd211b" id="u9ffd211b"><br></p>
  <p data-lake-id="u6c36e613" id="u6c36e613"><span data-lake-id="u0f3c8357" id="u0f3c8357">Spring的Bean是否线程安全，这个要取决于他的作用域。Spring的Bean有多种作用域，其中用的比较多的就是Singleton和Prototype。</span></p>
  <p data-lake-id="u62ad5892" id="u62ad5892"><span data-lake-id="ub098dd5c" id="ub098dd5c">​</span><br></p>
  <p data-lake-id="ubae86190" id="ubae86190"><span data-lake-id="u76b23041" id="u76b23041">默认情况下，Spring Bean 是单例的（Singleton）。这意味着在整个 Spring 容器中只存在一个 Bean 实例。如果将 Bean 的作用域设置为原型的（Prototype） ，那么每次从容器中获取 Bean 时都会创建一个新的实例。</span></p>
  <p data-lake-id="u148fc9a6" id="u148fc9a6"><span data-lake-id="uc6b213c3" id="uc6b213c3">​</span><br></p>
  <p data-lake-id="u7ac84b0e" id="u7ac84b0e"><span data-lake-id="u17371b90" id="u17371b90">对于Prototype这种作用域的Bean，他的Bean</span><span data-lake-id="u8bff0564" id="u8bff0564"> 实例不会被多个线程共享，所以不存在线程安全的问题。</span></p>
  <p data-lake-id="u9ef17959" id="u9ef17959"><span data-lake-id="u36d322e6" id="u36d322e6">​</span><br></p>
  <p data-lake-id="u2e03f9a3" id="u2e03f9a3"><span data-lake-id="u5cb72429" id="u5cb72429">但是对于Singleton的Bean，就可能存在线程安全问题了，但是也不绝对，要看这个Bean中是否有共享变量。</span></p>
  <p data-lake-id="u7a8db610" id="u7a8db610"><span data-lake-id="udd28e113" id="udd28e113">​</span><br></p>
  <p data-lake-id="uc9b52fd9" id="uc9b52fd9"><br></p>
  <p data-lake-id="ucef7e6cc" id="ucef7e6cc"><span data-lake-id="uaf5eeb9d" id="uaf5eeb9d">如以下Bean：</span></p>
  <p data-lake-id="u471f17fd" id="u471f17fd"><span data-lake-id="u5cba3803" id="u5cba3803">​</span><br></p>
  <pre lang="java"><code>
@Service
public class CounterService {
    private int count = 0;

    public int increment() {
        return ++count;
    }
}
</code></pre>
  <p data-lake-id="uc30a8ff0" id="uc30a8ff0"><br></p>
  <p data-lake-id="u63f3632f" id="u63f3632f"><span data-lake-id="u3dfef452" id="u3dfef452">默认情况下，Spring Bean 是单例的，count字段是一个共享变量，那么如果多个线程同时调用 increment 方法，可能导致计数器的值不正确。那么这段代码就不是线程安全的。</span></p>
  <p data-lake-id="u72765306" id="u72765306"><span data-lake-id="ue4a9d15a" id="ue4a9d15a">​</span><br></p>
  <p data-lake-id="u105d731d" id="u105d731d"><span data-lake-id="u27c76c95" id="u27c76c95">我们通常把上面这种Bean叫做有状态的Bean，有状态的Bean就是非线程安全的，我们需要自己来考虑他的线程安全性问题。</span></p>
  <p data-lake-id="u24eedfbb" id="u24eedfbb"><span data-lake-id="u9375fe68" id="u9375fe68">​</span><br></p>
  <p data-lake-id="u7ad20291" id="u7ad20291"><span data-lake-id="u4360d641" id="u4360d641">那如果一个Singleton的Bean中是无状态的，即没有成员变量，或者成员变量只读不写，那么他就是个线程安全的。</span></p>
  <p data-lake-id="ucc9156b4" id="ucc9156b4"><span data-lake-id="u0d7a2bb6" id="u0d7a2bb6">​</span><br></p>
  <pre lang="java"><code>
@Service
public class CounterService {
    
    public int increment(int a) {
        return ++a;
    }
}

</code></pre>
  <p data-lake-id="uc086a9d1" id="uc086a9d1"><br></p>
  <p data-lake-id="ud3ff92d8" id="ud3ff92d8"><span data-lake-id="u5bff207d" id="u5bff207d">所以，总结一下就是：</span></p>
  <p data-lake-id="ueb76336d" id="ueb76336d"><span data-lake-id="u25751578" id="u25751578">​</span><br></p>
  <p data-lake-id="ueb3651ad" id="ueb3651ad"><strong><span data-lake-id="uadcc6e20" id="uadcc6e20">Prototype的Bean是线程安全的，无状态的Singleton的Bean是线程安全的。有状态的Singleton的Bean是非线程安全的。</span></strong></p>
  <p data-lake-id="u1ed7585e" id="u1ed7585e"><span data-lake-id="u58702d56" id="u58702d56">​</span><br></p>
  <h1 data-lake-id="ci0a4" id="ci0a4"><span data-lake-id="u3b1b949f" id="u3b1b949f">扩展知识</span></h1>
  <p data-lake-id="uc3ffe816" id="uc3ffe816"><br></p>
  <h2 data-lake-id="Toe22" id="Toe22"><span data-lake-id="ue7d6159d" id="ue7d6159d">有状态的Bean如何解决线程安全问题</span></h2>
  <p data-lake-id="ub3d1faa7" id="ub3d1faa7"><br></p>
  <p data-lake-id="u36e19352" id="u36e19352"><span data-lake-id="u23bda356" id="u23bda356">想要让一个有状态的Bean变得线程安全，有以下几个做法：</span></p>
  <p data-lake-id="uc1e46268" id="uc1e46268"><span data-lake-id="udb83d623" id="udb83d623">​</span><br></p>
  <p data-lake-id="ud0174155" id="ud0174155"><span data-lake-id="u57b64427" id="u57b64427">1、修改作用域为Prototype，这样的Bean就可以避免线程安全问题。</span></p>
  <p data-lake-id="udb659131" id="udb659131"><span data-lake-id="u31330163" id="u31330163">​</span><br></p>
  <pre lang="java"><code>
@Scope("prototype")
@Service
public class CounterService {

  	private int count = 0;
    // ...
}
</code></pre>
  <p data-lake-id="uab988f05" id="uab988f05"><br></p>
  <p data-lake-id="u1f1611c2" id="u1f1611c2"><span data-lake-id="uf824c88f" id="uf824c88f">但是需要注意，Prototype的bean，每次从容器中请求一个 Prototype Bean 时，都会创建一个新的实例。这可能导致性能开销，特别是在需要频繁创建对象的情况下。 而且，每个 Prototype Bean 的实例都需要占用一定的内存，可能会导致内存资源的消耗较大。</span></p>
  <p data-lake-id="u63dbddae" id="u63dbddae"><br></p>
  <p data-lake-id="u29aaf96b" id="u29aaf96b"><span data-lake-id="u5c353f73" id="u5c353f73">2、加锁</span></p>
  <p data-lake-id="u6db07ca9" id="u6db07ca9"><span data-lake-id="u033ada64" id="u033ada64">​</span><br></p>
  <p data-lake-id="ua1596143" id="ua1596143"><span data-lake-id="ua9cb640c" id="ua9cb640c">想要实现线程安全，有一个有效的办法就是加锁，在并发修改共享变量的地方加锁：</span></p>
  <p data-lake-id="u53200653" id="u53200653"><span data-lake-id="ue200d986" id="ue200d986">​</span><br></p>
  <pre lang="java"><code>
@Service
public class CounterService {
    private int count = 0;

    public synchronized int increment() {
        return ++count;
    }
}
</code></pre>
  <p data-lake-id="u23e62d9c" id="u23e62d9c"><br></p>
  <p data-lake-id="u8e2202cc" id="u8e2202cc"><span data-lake-id="ue315c028" id="ue315c028">但是加锁的话会影响并发，降低系统的吞吐量，所以使用的时候需要谨慎，不建议用这个方案。</span></p>
  <p data-lake-id="u0f10a1f3" id="u0f10a1f3"><br></p>
  <p data-lake-id="u37f26ba3" id="u37f26ba3"><span data-lake-id="u92ca5f51" id="u92ca5f51">3、使用并发工具类</span></p>
  <p data-lake-id="ube071e0c" id="ube071e0c"><span data-lake-id="ua5f37fb9" id="ua5f37fb9">​</span><br></p>
  <p data-lake-id="u3f0471dd" id="u3f0471dd"><span data-lake-id="u10737c75" id="u10737c75">可以使用并发包中提供的工具类，如原子类，线程安全的集合等。</span></p>
  <p data-lake-id="u1a2b82bb" id="u1a2b82bb"><span data-lake-id="u4631d616" id="u4631d616">​</span><br></p>
  <pre lang="java"><code>
import java.util.concurrent.atomic.AtomicInteger;

public class CounterService {
    private AtomicInteger count = new AtomicInteger(0);

    public int increment() {
        return count.incrementAndGet();
    }
}

</code></pre>
  <p data-lake-id="uc456229c" id="uc456229c"><br></p>
  <p data-lake-id="ucf4eaad5" id="ucf4eaad5"><span data-lake-id="ubc26e95a" id="ubc26e95a">建议使用这种，既能保证线程安全，又有比较好的性能。</span></p>
 </body>
</html>